package com.cxx.food.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.cxx.food.constant.FileConstant;
import com.cxx.food.dto.file.ChunkInfoDto;
import com.cxx.food.dto.file.ChunkResultDto;
import com.cxx.food.dto.file.FileInfoDto;
import com.cxx.food.dto.file.FileRecordDto;
import com.cxx.food.entity.FileRecord;
import com.cxx.food.mapper.FileRecordMapper;
import com.cxx.food.service.FileService;
import com.cxx.food.util.FileUtils;
import com.cxx.food.util.UploadFileUtil;
import com.cxx.framework.web.CustomException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.*;


@Service
@Slf4j
public class FileServiceImpl implements FileService {
    @Value("${file.path}")
    private String fileRootPath;

    @Resource
    private FileRecordMapper fileRecordMapper;

    @Override
    public FileRecordDto uploadFile(MultipartFile multipartFile, String path, String md5) {
        UploadFileUtil.checkDirIsExist(getFilePath(path));

        String filename = multipartFile.getOriginalFilename();
        String newPath = path + File.separator + md5 + "." + FilenameUtils.getExtension(filename);
        File newFile = new File(getFilePath(newPath));

        saveFile(multipartFile, newFile);

        // 这里的url需要加上 WebMvcConfig 中设置的前缀
        String url = FileConstant.FILE_URL_PREFIX + File.separator + newPath;
        return getFileRecord(filename, md5, path, url);
    }

    @Override
    public Boolean uploadChunk(ChunkInfoDto chunkInfo, String path) {
        MultipartFile file = chunkInfo.getMultipartFile();

        try {
            // 1. 校验路径是否存在
            UploadFileUtil.checkDirIsExist(getFilePath(path));
            // 2. 获取文件块路径
            Path chunkPath = Paths.get(UploadFileUtil.generateChunkPath(getFilePath(path), chunkInfo));
            // 3. 将文件块写入指定路径
            Files.write(chunkPath, file.getBytes());
        } catch (IOException exception) {
            log.error("文件上传块失败: {}", exception.getMessage());
            throw new CustomException("文件上传块失败: " + exception.getMessage());
        }

        return true;
    }

    @Override
    public ChunkResultDto checkChunk(String identifier, String filename, String path) {
        ChunkResultDto chunkResult = new ChunkResultDto();
        List<Integer> uploadedChunkList = new ArrayList<>();

        String folder = getFilePath(path) + File.separator + identifier;
        String file = folder + File.separator + filename;

        // 判断文件夹是否存在
        if (UploadFileUtil.fileExists(folder)) {
            // 先判断整个文件是否已经上传过了，如果是，则告诉前端跳过上传，实现秒传
            if (UploadFileUtil.fileExists(file)) {
                chunkResult.setSkipUpload(true);
                chunkResult.setUploadedChunkList(uploadedChunkList);
                log.info("完整文件已存在，直接跳过上传，实现秒传");
            } else {
                chunkResult.setSkipUpload(false);
                // 获取已经上传的文件块
                chunkResult.setUploadedChunkList(UploadFileUtil.getUploadedChunkList(folder, filename));
            }
        } else {
            chunkResult.setSkipUpload(false);
            chunkResult.setUploadedChunkList(uploadedChunkList);
        }

        return chunkResult;
    }

    @Override
    public Boolean deleteChunk(String identifier, String path) {
        String folder = getFilePath(path) + File.separator + identifier;
        log.info("开始删除文件: " + folder);

        // 判断文件夹是否存在
        if (UploadFileUtil.fileExists(folder)) {
            UploadFileUtil.deleteDirectory(folder);
        } else {
            throw new CustomException("文件: " + folder + "不存在");
        }

        return true;
    }

    @Override
    public FileRecordDto mergeFile(String filename, String path) {
        String uuid = UUID.randomUUID().toString().replace("-", "");
        String newFilename = uuid + "." + FilenameUtils.getExtension(filename);
        try {
            UploadFileUtil.mergeFile(newFilename, getFilePath(path));
        } catch (IOException exception) {
            throw new CustomException("合并文件: " + filename + " 失败");
        }

        String newPath = path + File.separator + newFilename;
        String url = FileConstant.FILE_URL_PREFIX + File.separator + newPath;
        return getFileRecord(filename, uuid, path, url);
    }

    @Override
    public List<FileInfoDto> getFolderInfo(String folderName) {
        File folder = new File(folderName);
        List<FileInfoDto> fileInfoList = new ArrayList<>();
        if (folder.exists() && folder.isDirectory()) {
            File[] files = folder.listFiles();
            if (files != null) {
                List<FileInfoDto> folderInfoList = new ArrayList<>();
                List<FileInfoDto> fileInfoSubList = new ArrayList<>();
                for (File file : files) {
                    FileInfoDto fileInfo = new FileInfoDto();
                    fileInfo.setName(file.getName());
                    if (file.isDirectory()) {
                        fileInfo.setType("文件夹");
                        fileInfo.setSize("-");
                        folderInfoList.add(fileInfo);
                    } else {
                        fileInfo.setType(FileUtils.getFileType(file.getName()));
                        fileInfo.setSize(FileUtils.formatSize(file.length()));
                        fileInfoSubList.add(fileInfo);
                    }
                    fileInfo.setCreateDate(new Date(file.lastModified()));
                }
                fileInfoList.addAll(folderInfoList);
                fileInfoList.addAll(fileInfoSubList);
            }
        }
        return fileInfoList;
    }

    private void saveFile(MultipartFile multipartFile, File file) {
        // 如果文件不存在
        if (!file.exists()) {
            try {
                // 3. 如果不存在则创建文件
                multipartFile.transferTo(file);
            } catch (IOException e) {
                throw new CustomException("文件上传失败：{}", e.getMessage());
            }
        }
    }

    private FileRecordDto getFileRecord(String filename, String md5, String path, String url) {
        FileRecord fileRecord = new FileRecord();
        fileRecord.setName(filename);
        fileRecord.setMd5(md5);
        fileRecord.setPath(path);
        fileRecord.setUrl(url);
        fileRecord.setId(saveDatabase(fileRecord, md5));

        // 4. 返回文件信息
        FileRecordDto fileRecordDto = new FileRecordDto();
        BeanUtils.copyProperties(fileRecord, fileRecordDto);

        return fileRecordDto;
    }

    private Long saveDatabase(FileRecord fileRecord, String md5) {
        LambdaQueryWrapper<FileRecord> queryWrapper = Wrappers.lambdaQuery(FileRecord.class);
        FileRecord dbFileRecord = fileRecordMapper.selectOne(queryWrapper.eq(FileRecord::getMd5, md5));
        if (Objects.isNull(dbFileRecord)) {
            fileRecordMapper.insert(fileRecord);
            return fileRecord.getId();
        } else {
            return dbFileRecord.getId();
        }
    }

    private String getFilePath(String path) {
        return fileRootPath + path;
    }
}
